package com.zzuli.yxypre.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzuli.yxypre.comment.R;
import com.zzuli.yxypre.entity.*;
import com.zzuli.yxypre.mapper.*;
import com.zzuli.yxypre.service.UserService;
import com.zzuli.yxypre.utils.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @auther pony
 * @create 2023-04-30 21:05
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private LoveMapper loveMapper;

    @Autowired
    private SongMapper songMapper;

    @Autowired
    private SheetMapper sheetMapper;

    @Autowired
    private AlbumMapper albumMapper;

    @Autowired
    private PlayCountMapper playCountMapper;



    // 登录验证码 redis key 前缀
    private String loginRedisKeyPre = "yxy-pre/login/verifyCode:";

    // 注册验证码 redis key 前缀
    private String registerRedisKeyPre = "yxy-pre/register/verifyCode:";

    // 用户注册默认头像
    private String img = "https://pony-1312090731.cos.ap-beijing.myqcloud.com/yxy/images/user-default.jpg";

    /**
     * 获取登录短信验证码
     * @return
     */
    @Override
    public R getLoginVerifyCode(HttpServletRequest request) {
        //1、使用 hutool 工具类生成随机验证码（记得引依赖）
        String code = RandomUtil.randomNumbers(6);
        //2、发送短信验证码
        boolean flag = AlibabaSendSmTestUtils.sendSm(code);
        //3、将验证码保存到redis，设置有效期为 3 min
        redisTemplate.opsForValue().set(getCodeKey(loginRedisKeyPre,request), code, 3 * 60, TimeUnit.SECONDS);
        if (flag){
            return R.success().message("短信验证码，发送成功，注意查收！");
        }
        return R.error().message("系统错误，请联系管理员！");
    }

    /**
     * 获取注册短信验证码
     * @param request
     * @return
     */
    @Override
    public R getRegisterVerifyCode(HttpServletRequest request) {
        //1、使用 hutool 工具类生成随机验证码（记得引依赖）
        String code = RandomUtil.randomNumbers(6);
        //2、发送短信验证码
        boolean flag = AlibabaSendSmTestUtils.sendSm(code);
        //3、将验证码保存到redis，设置有效期为 3 min
        redisTemplate.opsForValue().set(getCodeKey(registerRedisKeyPre,request), code, 3 * 60, TimeUnit.SECONDS);
        if (flag){
            return R.success().message("短信验证码，发送成功，注意查收！");
        }
        return R.error().message("系统错误，请联系管理员！");
    }

    /**
     * 用户注册-短信验证码注册
     * @param code 验证码
     * @param phone 手机号
     * @return
     */
    @Override
    public R registerByPhone(String code, String phone, HttpServletRequest request) {
        //1、判断是否由此用户，根据 phone 查询数据库
        LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();
        qw.eq(User::getPhone, phone);
        qw.last("limit 1");
        User selectResult = userMapper.selectOne(qw);
        // 存在
        if (!Objects.isNull(selectResult)){
            return R.error().message("此手机号已经注册，请前往登录页面");
        }
        //2、判断验证码是否正确
        // 获取 redis 中验证码的 key
        String codeKey = getCodeKey(registerRedisKeyPre, request);
        //根据 key 值取 验证码
        String value = String.valueOf(redisTemplate.opsForValue().get(codeKey));
        if (value==null){
            return R.error().message("验证码过期");
        }
        if (!code.equals(value)){
            return R.error().message("验证码错误");
        }

        //3、添加用户
        User user = new User();
        user.setPhone(phone);
        // 默认头像
        user.setImg(img);
        // 默认昵称
        user.setNickName("yxy-user"+RandomUtil.randomNumbers(6));
        userMapper.insert(user);

        return R.success().message("注册成功！");
    }

    /**
     * 修改用户是否喜欢
     * @param type 关联表的类型 1：歌曲 2：专辑 3：歌单
     * @param flag 要修改成的字段 1：喜欢 2：不喜欢
     * @param relevance 关联字段格局type的不同关联的也不同 1：歌曲id 2：专辑id 3：歌单id
     * @param request
     * @return
     */
    @Override
    public R changeLove(Integer type, Integer flag, Integer relevance, HttpServletRequest request) {
        // 获取登录用户id
        Integer userId = Integer.valueOf(JwtUtils.getUserIdByJwtToken(request));
        // 添加我喜欢
        if (flag==1){
            LambdaQueryWrapper<Love> qw = new LambdaQueryWrapper<>();
            qw.eq(Love::getRelevance, relevance);
            qw.eq(Love::getType, type);
            qw.eq(Love::getUserId, userId);
            Love addLove = loveMapper.selectOne(qw);
            if (Objects.isNull(addLove)){
                Love love = new Love();
                love.setUserId(userId);
                love.setRelevance(relevance);
                love.setType(type);
                loveMapper.insert(love);
            }
        }else if(flag==0){ //删除我喜欢
            LambdaQueryWrapper<Love> qw = new LambdaQueryWrapper<>();
            qw.eq(Love::getRelevance, relevance);
            qw.eq(Love::getType, type);
            qw.eq(Love::getUserId, userId);
            loveMapper.delete(qw);
        }
        return R.success().message("修改用户我喜欢成功！");
    }

    /**
     * 获取登录用户喜欢的歌曲或歌单或专辑
     * @param request
     * @return
     */
    @Override
    public R getLove(Integer type, HttpServletRequest request) {
        // 获取登录用户id
        Integer userId = Integer.valueOf(JwtUtils.getUserIdByJwtToken(request));
        LambdaQueryWrapper<Love> loveQw = new LambdaQueryWrapper<>();
        loveQw.eq(Love::getType, type);
        loveQw.eq(Love::getUserId, userId);
        List<Love> loveList = loveMapper.selectList(loveQw);

        List<Song> songList = null;
        List<Album> albumList = null;
        List<Sheet> sheetList = null;

        if (type==1){ //是歌曲
            songList = new ArrayList<>();
            for (Love love : loveList) {
                LambdaQueryWrapper<Song> qw = new LambdaQueryWrapper<>();
                qw.eq(Song::getSongId, love.getRelevance());
                Song song = songMapper.selectOne(qw);
//                if (!Objects.isNull(song)){
                    // 封装歌曲的歌手信息
                    List<Singer> songSingerList = songMapper.selectSingerBySongId(song.getSongId());
                    song.setSinger(songSingerList);
                    // 封装歌曲的专辑信息
                    List<Album> songAlbumList = songMapper.selectAlbumBySongId(song.getSongId());
                    song.setAlbum(songAlbumList);
                    // 封装歌曲是否被用户喜欢
                    LambdaQueryWrapper<Love> loveQw1 = new LambdaQueryWrapper<>();
                    // 歌曲 type 是 1
                    loveQw1.eq(Love::getType, 1);
                    loveQw1.eq(Love::getUserId, userId);
                    loveQw1.eq(Love::getRelevance, song.getSongId());
                    Love love1 = loveMapper.selectOne(loveQw1);
                    if (!Objects.isNull(love1)){
                        song.setIsLove(1);
                    }else {
                        song.setIsLove(0);
                    }
//                }
                songList.add(song);
            }
            return R.success().message("登录用户-我喜欢-歌曲获取成功").data("songList",songList);
        }else if(type==2){ //是专辑
            albumList = new ArrayList<>();
            for (Love love : loveList) {
                LambdaQueryWrapper<Album> qw = new LambdaQueryWrapper<>();
                qw.eq(Album::getAlbumId, love.getRelevance());
                Album album = albumMapper.selectOne(qw);
                albumList.add(album);
            }
            return R.success().message("登录用户-我喜欢-专辑获取成功").data("albumList", albumList);
        }else if(type==3){ //是歌单
            sheetList = new ArrayList<>();
            for (Love love : loveList) {
                LambdaQueryWrapper<Sheet> qw = new LambdaQueryWrapper<>();
                qw.eq(Sheet::getId, love.getRelevance());
                Sheet sheet = sheetMapper.selectOne(qw);
                // 封装 歌单创建用户信息
                if (!Objects.isNull(sheet)){
                    List<User> userList = sheetMapper.selectUserBySheetId(sheet.getId(),userId);
                    sheet.setUserList(userList);
                }

                sheetList.add(sheet);
            }
            return R.success().message("登录用户-我喜欢-歌单获取成功").data("sheetList", sheetList);
        }
        return R.error().message("系统错误，请联系管理员");
    }

    /**
     * 获取登录用户创建的歌单
     * @param request
     * @return
     */
    @Override
    public R getSheet(HttpServletRequest request) {
        // 获取登录用户id
        Integer userId = Integer.valueOf(JwtUtils.getUserIdByJwtToken(request));
        LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();
        qw.eq(User::getId, userId);
        User user = userMapper.selectOne(qw);
        List<User> userList = new ArrayList<>();
        userList.add(user);
        List<Sheet> sheetList = userMapper.selectSheetByUserId(userId);
        // 封装 sheet 数据
        for (Sheet sheet : sheetList) {
            sheet.setUserList(userList);
        }
        return R.success().message("当前登录用户创建的歌单").data("sheetList", sheetList);
    }

    /**
     * 获取登录用户可能喜欢的歌单
     * @param request
     * @return
     */
    @Override
    public R maybeLove(HttpServletRequest request) {
        // 1、用户喜欢的标签
        HashSet<String> userLoveSheetType = getUserLoveSheetType(1);
        System.out.println("################################################");
        for (String s : userLoveSheetType) {
            System.out.println("标签:"+s);
        }

        // 2、建立所有歌单倒排索引 Map
        HashMap<String, Set<Integer>> sheetMap = getSheetMap(1);

        // 3、推荐：用户可能会喜欢的歌单
        HashSet<Sheet> userLoveSheetList = new HashSet<>();
        for (String tag : userLoveSheetType) {
            Set<Integer> sheetIdList = sheetMap.get(tag);

            if (sheetIdList!=null&&sheetIdList.size()!=0){
                for (Integer sheetId : sheetIdList) {
                    Sheet sheet = sheetMapper.selectById(sheetId);
                    userLoveSheetList.add(sheet);
                }
            }
        }

        // 4、根据歌单周播放量降序排序
        for (Sheet sheet : userLoveSheetList) {
            // 默认播放量是0
            sheet.setPlayCount(0);

            // 查询实际播放量
            LambdaQueryWrapper<PlayCount> pcQw = new LambdaQueryWrapper<>();
            pcQw.eq(PlayCount::getType, 3);
            pcQw.eq(PlayCount::getRelevance, sheet.getId());
            PlayCount playCount = playCountMapper.selectOne(pcQw);
            if (!Objects.isNull(playCount)){
                sheet.setPlayCount(playCount.getWeekCount());
            }
        }
        List<Sheet> list = new ArrayList<>(userLoveSheetList);
        Collections.sort(list);

        for (Sheet sheet : list) {
            System.out.println("推荐的歌曲：" + sheet);
        }
        return R.success().message("推荐歌单").data("loveSheetList", list);
    }

    /**
     * 获取用户喜欢歌单的标签
     * @param userId
     * @return
     */
    public HashSet<String> getUserLoveSheetType(Integer userId){
        // 2、查询登录用户喜欢的歌曲所在歌单的标签
        LambdaQueryWrapper<Love> loveQw = new LambdaQueryWrapper<>();
        loveQw.eq(Love::getType, 3);
        loveQw.eq(Love::getUserId, userId);
        List<Love> loveList = loveMapper.selectList(loveQw);
        HashSet<String> typeSet = new HashSet<>();
        for (Love love : loveList) {
            // 2-1、查询用户新欢的歌曲
            LambdaQueryWrapper<Sheet> qw = new LambdaQueryWrapper<>();
            qw.eq(Sheet::getId, love.getRelevance());
            Sheet sheet = sheetMapper.selectOne(qw);
            String type = sheet.getType();
            if (type!=null&&!"".equals(type)){
                // string 转 数组
                String [] tagArr= type.split(",");
                //遍历数组并存入集合,如果元素已存在则不会重复存入
                for (int i = 0; i < tagArr.length; i++) {
                    typeSet.add(tagArr[i]);
                }
            }
        }
        return typeSet;
    }


    /**
     * 对所有标签不为空的歌单（不包括登录用户已经添加到我喜欢的）建立倒排索引
     * @param userId
     * @return
     */
    HashMap<String, Set<Integer>> getSheetMap(Integer userId){
        // 防止value id 重复，使用 set 集合
        HashMap<String, Set<Integer>> sheetMap = new HashMap<>();
        // 查询所有标签不为空的歌单列表
        List<Sheet> allSheetList = sheetMapper.selectTypeNotNull();
        // 查询其中不是当前登录用户的歌单列表（这里没必要这么麻烦，可以使用stream过滤进行优化）
        ArrayList<Sheet> sheetList = new ArrayList<>();
        for (Sheet sheet : allSheetList) {
            List<User> users = sheetMapper.selectUserBySheetId(sheet.getId(), userId);
            if (users.size()==0){
                sheetList.add(sheet);
            }
        }
        // 存储带有标签的歌单 id
        HashSet<Integer> idSet = new HashSet<>();
        for (Sheet sheet : sheetList) {
            // 一个歌单不止一个标签，例如，"爱情，古典"
            String type = sheet.getType();
            idSet.add(sheet.getId());
            if (type!=null&&!"".equals(type)){
                // string 转 数组
                String [] tagArr= type.split(",");
                // 遍历数组并存入集合,如果元素已存在则不会重复存入
                for (int i = 0; i < tagArr.length; i++) {
                    if (sheetMap.containsKey(tagArr[i])){
                        sheetMap.put(tagArr[i], idSet);
                    }else {
                        // 第一次添加标签，先创建一个set，存一下id
                        HashSet<Integer> firstIds = new HashSet<>();
                        firstIds.add(sheet.getId());
                        sheetMap.put(tagArr[i], firstIds);
                    }
                }
            }
        }
        return sheetMap;
    }

    /**
     * 用户登录-短信验证码登录
     * @param code 验证码
     * @param phone 手机号
     * @return
     */
    @Override
    public R loginByPhone(String code, String phone, HttpServletRequest request) {
        //1、判断是否由此用户，根据 phone 查询数据库
        LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();
        qw.eq(User::getPhone, phone);
        qw.last("limit 1");
        User selectResult = userMapper.selectOne(qw);
        if (Objects.isNull(selectResult)){
            return R.error().message("此手机号还未注册");
        }
        //2、判断验证码是否正确
        // 获取 redis 中验证码的 key
        String codeKey = getCodeKey(loginRedisKeyPre, request);
        //根据 key 值取 验证码
        String value = String.valueOf(redisTemplate.opsForValue().get(codeKey));
        if (value==null){
            return R.error().message("验证码过期");
        }
        if (!code.equals(value)){
            return R.error().message("验证码错误");
        }

        //3、如果用户登录成功，根据用户 id 生成 token，返回前端
        String token = JwtUtils.getJwtToken(String.valueOf(selectResult.getId()));

        return R.success().message("登录成功！").data("token", token);
    }

    /**
     * 用户登录-账号密码登录
     * @param phone
     * @param password
     * @param request
     * @return
     */
    @Override
    public R loginByPwd(String phone, String password, HttpServletRequest request) {
        //1、判断是否由此用户，根据 phone 查询数据库
        LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();
        qw.eq(User::getPhone, phone);
        qw.last("limit 1");
        User selectResult = userMapper.selectOne(qw);
        if (Objects.isNull(selectResult)){
            return R.error().message("此手机号还未注册");
        }
        //2、判断此用户是否设置了密码
        String resultPassword = selectResult.getPassword();
        if ("".equals(resultPassword)||resultPassword==null){
            return R.error().message("您还未设置密码，请先使用短信验证码登录");
        }

        //3、将用户表单传来的密码进行md5加码后与数据库里的密码进行比较
        String md5Password = Md5Utils.getMD5(password);
        if(!resultPassword.equals(md5Password)){
            return R.error().message("登录失败，密码错误！");
        }

        //4、 密码正确，生成 token，返回前端
        String token = JwtUtils.getJwtToken(String.valueOf(selectResult.getId()));

        return R.success().message("登录成功！").data("token", token);
    }


    /**
     * 通过 token，获取登录用户信息
     * @param request
     * @return
     */
    @Override
    public R getUserInfo(HttpServletRequest request) {
        //判断 token 是否为 null
        System.out.println(request.getHeader("token"));
        if(request.getHeader("token")!=null&&!"".equals(request.getHeader("token"))) {
            // 根据 token 获取登录用户的id
            String id = String.valueOf(JwtUtils.getUserIdByJwtToken(request));
            System.out.println(id);
            // 根据 id 查询数据库获取登录用户信息
            LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();
            qw.eq(User::getId, id);
            User selectResult = userMapper.selectOne(qw);

            // 封装登录用户我喜欢的歌曲总数
            LambdaQueryWrapper<Love> loveSongQw = new LambdaQueryWrapper<>();
            // 歌曲 type 是 1
            loveSongQw.eq(Love::getType, 1);
            loveSongQw.eq(Love::getUserId, selectResult.getId());
            Integer songCount = loveMapper.selectCount(loveSongQw);
            selectResult.setLoveSongCount(songCount);

            // 封装登录用户我喜欢的专辑总数
            LambdaQueryWrapper<Love> loveAlbumQw = new LambdaQueryWrapper<>();
            // 专辑 type 是 2
            loveAlbumQw.eq(Love::getType, 2);
            loveAlbumQw.eq(Love::getUserId, selectResult.getId());
            Integer albumCount = loveMapper.selectCount(loveAlbumQw);
            selectResult.setLoveAlbumCount(albumCount);

            // 封装登录用户我喜欢的歌单总数
            LambdaQueryWrapper<Love> loveSheetQw = new LambdaQueryWrapper<>();
            // 专辑 type 是 3
            loveSheetQw.eq(Love::getType, 3);
            loveSheetQw.eq(Love::getUserId, selectResult.getId());
            Integer sheetCount = loveMapper.selectCount(loveSheetQw);
            selectResult.setLoveSheetCount(sheetCount);

            // 封装登录用户创建的歌单总数
            List<Sheet> sheetList = userMapper.selectSheetByUserId(Integer.valueOf(id));
            selectResult.setSheetCount(sheetList.size());

            return R.success().message("根据token获取登录用户信息").data("userInfo", selectResult);
        }
        return R.success().message("根据token获取登录用户信息").data("userInfo", null);
    }

    /**
     * 验证码 存入redis缓存的 key 值
     * 通过 用户IP + 用户信息 加密后作为 key 值
     * @param request
     * @return
     */
    public String getCodeKey(String keyPre, HttpServletRequest request){
        //1、调用工具方法获取 ip
        String ip = IpUtils.getIpAddr(request);
        /*2、获取用户信息，User-Agent，网站服务器通过识别 “UA”来确定用户所使用
         的操作系统版本、CPU 类型、浏览器版本等信息。*/
        String userInfo = request.getHeader("User-Agent");
        //3、将 ip 和 用户信息 userInfo 加密作为 key
        String key = DigestUtils.md5DigestAsHex((ip+userInfo).getBytes());
        //4、设置前缀并返回
        return keyPre + key;
    }


    /**
     * 获取一条歌曲信息
     * @param request
     * @return
     */
    @Override
    public R getOneSong(String key, HttpServletRequest request) {
        // 根据 token 获取登录用户的id
        String userId = String.valueOf(JwtUtils.getUserIdByJwtToken(request));
        LambdaQueryWrapper<Song> songQw = new LambdaQueryWrapper<>();
        songQw.like(Song::getSongName, key);
        songQw.last("limit 1");
        Song song = songMapper.selectOne(songQw);

        // 封装歌曲信息
        // 判断歌曲是否被当前登录用户所喜欢
        LambdaQueryWrapper<Love> loveQw1 = new LambdaQueryWrapper<>();
        // 歌曲 type 是 1
        loveQw1.eq(Love::getType, 1);
        loveQw1.eq(Love::getUserId, userId);
        loveQw1.eq(Love::getRelevance, song.getSongId());
        Love love1 = loveMapper.selectOne(loveQw1);
        if (!Objects.isNull(love1)){
            song.setIsLove(1);
        }else {
            song.setIsLove(0);
        }

        // 封装歌曲的歌手信息
        // 查询歌手
        List<Singer> singerList = songMapper.selectSingerBySongId(song.getSongId());
        song.setSinger(singerList);
        // 查询专辑
        List<Album> albumList = songMapper.selectAlbumBySongId(song.getSongId());
        song.setAlbum(albumList);

        return R.success().message("单个歌曲数据").data("songInfo", song);
    }

    @Override
    public R login() {
        return null;
    }

}
